package top.dyzmj.detty.core.utils.concurrent;

import top.dyzmj.detty.core.utils.internal.InternalThreadLocalMap;
import top.dyzmj.detty.core.utils.internal.PlatformDependent;

import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;

/**
 * 描述:
 * 一个特殊的ThreadLocal变体，当从FastThreadLocalThread访问时，它可以获得更高的访问性能。
 * 在内部，FastThreadLocal使用数组中的常量索引来查找变量，而不是使用哈希代码和哈希表。
 * 尽管看起来非常微妙，但与使用哈希表相比，它产生了轻微的性能优势，并且在频繁访问时非常有用。
 * 要利用这个线程局部变量，你的线程必须是FastThreadLocalThread或它的子类型。
 * 由于这个原因，默认情况下，所有由DefaultThreadFactory创建的线程都是FastThreadLocalThread。
 * 请注意，快速路径只可能在扩展FastThreadLocalThread的线程上，因为它需要一个特殊的字段来存储必要的状态。
 * 任何其他类型的线程的访问返回到常规的ThreadLocal。
 *
 * @author dongYu
 * @date 2021/11/24
 */
public class FastThreadLocal<V> {

	private static final int VARIABLES_TO_REMOVE_INDEX = InternalThreadLocalMap.nextVariableIndex();

	private final int index;

	public FastThreadLocal() {
		index = InternalThreadLocalMap.nextVariableIndex();
	}

	/**
	 * 移除所有绑定到当前线程的FastThreadLocal变量。
	 * 当您处于容器环境中，并且不想将线程局部变量留在您不管理的线程中时，此操作非常有用。
	 */
	public static void removeAll() {
		InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
		if (threadLocalMap == null) {
			return;
		}

		try {
			Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX);
			if (v != null && v != InternalThreadLocalMap.UNSET) {
				@SuppressWarnings("unchecked")
				Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
				FastThreadLocal<?>[] variablesToRemoveArray =
						variablesToRemove.toArray(new FastThreadLocal[0]);
				for (FastThreadLocal<?> tlv : variablesToRemoveArray) {
					tlv.remove(threadLocalMap);
				}
			}
		} finally {
			InternalThreadLocalMap.remove();
		}
	}

	/**
	 * 返回绑定到当前线程的线程局部变量的数量
	 */
	public static int size() {
		InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
		if (threadLocalMap == null) {
			return 0;
		} else {
			return threadLocalMap.size();
		}
	}

	/**
	 * 破坏保持所有FastThreadLocal变量从非fastthreadlocalthreads访问的数据结构。
	 * 当您在容器环境中，并且不想将线程局部变量留在不管理的线程中时，此操作非常有用。
	 * 当应用程序从容器中卸载时调用此方法。
	 */
	public static void destroy() {
		InternalThreadLocalMap.destroy();
	}

	@SuppressWarnings("unchecked")
	private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
		Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX);
		Set<FastThreadLocal<?>> variablesToRemove;
		if (v == InternalThreadLocalMap.UNSET || v == null) {
			variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
			threadLocalMap.setIndexedVariable(VARIABLES_TO_REMOVE_INDEX, variablesToRemove);
		} else {
			variablesToRemove = (Set<FastThreadLocal<?>>) v;
		}

		variablesToRemove.add(variable);
	}

	private static void removeFromVariablesToRemove(
			InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {

		Object v = threadLocalMap.indexedVariable(VARIABLES_TO_REMOVE_INDEX);

		if (v == InternalThreadLocalMap.UNSET || v == null) {
			return;
		}

		@SuppressWarnings("unchecked")
		Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
		variablesToRemove.remove(variable);
	}

	/**
	 * 返回当前线程的值
	 */
	public final V get() {
		InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
		Object v = threadLocalMap.indexedVariable(index);
		if (v != InternalThreadLocalMap.UNSET) {
			return (V) v;
		}

		return initialize(threadLocalMap);
	}

	/**
	 * 返回当前线程的当前值(如果存在)，否则为空。
	 */
	@SuppressWarnings("unchecked")
	public final V getIfExists() {
		InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
		if (threadLocalMap != null) {
			Object v = threadLocalMap.indexedVariable(index);
			if (v != InternalThreadLocalMap.UNSET) {
				return (V) v;
			}
		}
		return null;
	}

	/**
	 * 设置当前线程的值
	 */
	public final void set(V value) {
		if (value != InternalThreadLocalMap.UNSET) {
			InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
			setKnownNotUnset(threadLocalMap, value);
		} else {
			remove();
		}
	}

	/**
	 * 为指定的线程本地映射设置值。
	 * 指定的线程本地映射必须是当前线程的。
	 */
	public final void set(InternalThreadLocalMap threadLocalMap, V value) {
		if (value != InternalThreadLocalMap.UNSET) {
			setKnownNotUnset(threadLocalMap, value);
		} else {
			remove(threadLocalMap);
		}
	}

	public final void remove() {
		remove(InternalThreadLocalMap.getIfSet());
	}

	public final void remove(InternalThreadLocalMap threadLocalMap) {
		if (threadLocalMap == null) {
			return;
		}
		Object v = threadLocalMap.removeIndexedVariable(index);
		removeFromVariablesToRemove(threadLocalMap, this);

		if (v != InternalThreadLocalMap.UNSET) {
			try {
				onRemoval((V) v);
			} catch (Exception e) {
				PlatformDependent.throwException(e);
			}
		}
	}

	/**
	 * 当且仅当这个线程局部变量被设置时返回true。
	 */
	public final boolean isSet() {
		return isSet(InternalThreadLocalMap.getIfSet());
	}

	/**
	 * 当且仅当这个线程局部变量被设置时返回true。
	 * 指定的线程本地映射必须是当前线程的。
	 */
	public final boolean isSet(InternalThreadLocalMap threadLocalMap) {
		return threadLocalMap != null && threadLocalMap.isIndexedVariableSet(index);
	}

	private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
		if (threadLocalMap.setIndexedVariable(index, value)) {
			addToVariablesToRemove(threadLocalMap, this);
		}
	}

	/**
	 * 初始化线程局部变量
	 */
	private V initialize(InternalThreadLocalMap threadLocalMap) {
		V v = null;
		try {
			v = initialValue();
		} catch (Exception e) {
			PlatformDependent.throwException(e);
		}

		threadLocalMap.setIndexedVariable(index, v);
		addToVariablesToRemove(threadLocalMap, this);
		return v;
	}

	/**
	 * 返回该线程局部变量的初始值。
	 */
	protected V initialValue() {
		return null;
	}

	/**
	 * 当这个线程局部变量被remove()移除时调用。
	 * 注意，remove()并不能保证在' Thread '完成时被调用，
	 * 这意味着在' Thread '完成的情况下，你不能依赖它来清理资源
	 */
	protected void onRemoval(@SuppressWarnings("UnusedParameters") V value) {
	}


}
